/* * 作者:蘇文宏. * 學號:8324057 * 建議使用有 jit 的 jvm 執行,否則速度會很慢. */ import java.util.Random; import java.awt.*; import java.awt.image.*; public class DrawPanel extends Panel implements Runnable{ MyPanel parent;//父容器是誰. Random rand;//亂數. int r,g,b,r1,g1,b1;//線段顏色用變數. int x1,y1,x2,y2;//線段用之變數. int bx,by;//透明球用之變數. final int R_DIRECTION = 3;//紅色一次遞增量. final int G_DIRECTION = 5;//綠色一次遞增量. final int B_DIRECTION = 4;//藍色一次遞增量. final int X_DIRECTION = 5;//水平方向一次遞增量. final int Y_DIRECTION = 4;//垂直方向一次遞增量. final int CIRCLE_COUNTER = 3;//幾次要畫出一個透明球. int DELAY = 3;//每畫一次,延遲的時間,單位:百萬分之一秒. double rotaryAngle = 1d;//旋轉角度. double cosR = Math.cos(rotaryAngle);//先計算,節省時間! double sinR = Math.sin(rotaryAngle); double translateAngle = 0d;//移位角度. double cosT = Math.cos(translateAngle);//先計算,節省時間! double sinT = Math.sin(translateAngle); final int KEEP_COLOR = 0;//保留不使用的顏色數目. final int LINE_COLOR_BOUNDS = (256 - KEEP_COLOR) * 2;//顏色的邊界. boolean isStop = false;//給執行緒使用的旗標. int ballxDirection = 8;//透明球 x 方向一次遞增量. int ballyDirection = 6;//透明球 y 方向一次遞增量. int xsize,ysize;//此畫面之最大寬度,高度. int radius;//透明球之球半徑. int dstPixels[],srcPixels[];//存放透明球和背景的圖素值. double redFactor,greenFactor,blueFactor;//透明球的漸層顏色資料. Rectangle ball,backBall;//透明球、和背景的資料. Point p1,p2;//線段的兩點. Thread drawer;//執行緒. Image backImage = null; //此畫面的備份. Graphics backImageGc = null,mainGc = null;//備份畫面的繪圖者,及此畫面的繪圖者. Runtime runtime = Runtime.getRuntime(); DrawPanel(MyPanel parent){ this.parent = parent; rand = new Random(); //顏色初使化. r = r1 = random(LINE_COLOR_BOUNDS); g = g1 = random(LINE_COLOR_BOUNDS); b = b1 = random(LINE_COLOR_BOUNDS); //線段座標初使化. x1 = random(200); y1 = random(200); x2 = random(200); y2 = random(200); p1 = new Point(x1,y1); p2 = new Point(x2,y2); //透明球初使化. radius = 30; br = br1 = random((int)(0.3 * 255)); bg = bg1 = random((int)(0.3 * 255)); bb = bb1 = random((int)(0.3 * 255)); ball = new Rectangle(bx = random(200),by = random(200),radius * 2,radius * 2); backBall = new Rectangle(radius * 2,radius * 2); } //傳回正整數的亂數函式. int random(int bounds){ if(bounds == 0) return 0; else return Math.abs(rand.nextInt()) % bounds; } public void paint(Graphics g){ start(); } //開始繪圖. public void start(){ drawer=new Thread(this); isStop=false; drawer.start(); } //停止繪圖. public void stop(){ //釋放變數. isStop = true; drawer = null; mainGc.dispose(); mainGc = null; backImageGc.dispose(); backImageGc = null; srcPixels = null; dstPixels = null; } FontMetrics fm; //執行緒之主體. public void run(){ //取得現在時間. long time = System.currentTimeMillis(); //取得寬度、高度. xsize = bounds().width; ysize = bounds().height; //第一次、或者是改變寬度、高度時,再重新分配. if((backImage==null) || (backImage.getWidth(this)!=xsize) || (backImage.getHeight(this)!=ysize)){ backImage = createImage(xsize,ysize); backImageGc = backImage.getGraphics(); mainGc = getGraphics(); backImageGc.setFont(new Font("",Font.PLAIN,20)); fm = backImageGc.getFontMetrics(); } //雙重繪圖區之一. backImageGc.clearRect(0,0,xsize,ysize); //透明球的雙重繪圖變數. Image subImage=null,keepSubImage=null; subImage = breateBall(); keepSubImage = createImage(new MemoryImageSource(backBall.width ,backBall.height , srcPixels ,0 ,backBall.width)); int times = 0; //執行緒的工作時機. while(Thread.currentThread() == drawer && !isStop)try{ //把彩色線段繪出. if(parent.isDrawLine){ drawLine(backImageGc); } //把緩衝區繪出. mainGc.drawImage(backImage,0,0,parent); time += DELAY; //每 CIRCLE_COUNTER 次繪出一次透明球. if(parent.isDrawBall){ if(times > CIRCLE_COUNTER){ backImageGc.drawImage(keepSubImage,ball.x,ball.y,backBall.width,backBall.height,parent); showString(backImageGc); changeBallColor(); changeBallLocation(); subImage = breateBall(); keepSubImage = createImage(new MemoryImageSource(backBall.width ,backBall.height , srcPixels ,0 ,backBall.width)); backImageGc.drawImage(subImage,ball.x,ball.y,ball.width,ball.height,parent); //因為需要大量的記憶體,所以必須時常使用 gc() System.gc(); times = 0; } else times++; }//end of 假如要畫透明球. Thread.sleep(Math.max(0,time - System.currentTimeMillis())); } catch(InterruptedException e){ } catch(OutOfMemoryError e){ //如果記憶體不足,停止動作 3 秒,讓 gc() 清除記憶體中的垃圾. System.gc(); try{ Thread.sleep(3000); } catch(InterruptedException e1){ } } } int xs = -1,ys = -1; //顯示一些資訊. void showString(Graphics gc){ int rows = 5; String str1 = " 透明之球 "; String str2 = " width = " + xsize +", height = " + ysize + " "; String str3 = " Free memory = " ; if(xs < 0 || ys < 0){ xs = 30; ys = (ysize - fm.getHeight() * rows) / 3; } //顯示字串一. gc.setColor(Color.red); gc.fill3DRect(xs,ys,fm.stringWidth(str1),fm.getHeight(),true); gc.setColor(Color.blue); gc.drawString(str1,xs,ys + fm.getAscent()); //顯示字串二. gc.setColor(Color.blue); gc.fill3DRect(xs,ys+fm.getHeight()*2,fm.stringWidth(str2),fm.getHeight(),false); gc.setColor(Color.red); gc.drawString(str2, xs,ys + fm.getAscent() + fm.getHeight()*2); //顯示字串三. String str = str3 + runtime.freeMemory() + " "; gc.setColor(Color.green); gc.fill3DRect(xs,ys+fm.getHeight()*4,fm.stringWidth(str3) * 2,fm.getHeight(),false); gc.setColor(Color.black); gc.drawString(str, xs,ys + fm.getAscent() + fm.getHeight()*4); } PixelGrabber pg; //建立透明球. Image breateBall(){ //備份透明球之背景. backBall.reshape(ball.x ,ball.y ,ball.width, ball.height); //第一次、或者是改變陣列大小時,再重新分配. if(srcPixels == null || srcPixels.length < ball.width * ball.height){ srcPixels = new int[backBall.width * backBall.height]; dstPixels = new int[ball.width * ball.height]; } //將影像轉成陣列. pg = new PixelGrabber(backImage ,backBall.x ,backBall.y ,backBall.width ,backBall.height , srcPixels ,0 ,backBall.width); try{ pg.grabPixels(); } catch(InterruptedException e){ } catch(Error e){ } //計算透明球內部的光線曲折程度. int x0 = radius,y0 = radius; int index = 0; double x,y; int distance; int squareRadius = radius * radius; for(int j=0;j<ball.height;j++) for(int i=0;i<ball.width;i++,index++){ x = i - x0; y = y0 - j; distance = (int)(long)(x*x + y*y); if(distance < squareRadius){ double l = Math.sqrt(distance); double scale = l / radius;//放大. //scale = Math.sin(scale * Math.PI / 2);//放大. //scale = Math.random();//亂數 如塵土飛揚. //scale = - scale;//巔倒並放大. int xc = x0 + (int)Math.round(x * scale);//放大或縮小. int yc = y0 - (int)Math.round(y * scale); if(xc < ball.width && yc < ball.height && xc >= 0 && yc >= 0){ ///* 有深淺顏色變化之透明球. double factor = (radius - l) / radius; double rFactor = factor * redFactor + (1 - redFactor); double gFactor = factor * greenFactor + (1 - greenFactor); double bFactor = factor * blueFactor + (1 - blueFactor); int value = srcPixels[xc + yc * ball.width]; int color = (((value >> 24) & 0xff) << 24) + ((int)Math.round(((value >> 16) & 0xff) * rFactor) << 16) + ((int)Math.round(((value >> 8) & 0xff) * gFactor) <<8) + ((int)Math.round((value & 0xff) * bFactor)) ; dstPixels[index] = color; // */ /* 完全透明的透明球. dstPixels[index] = srcPixels[xc + yc * ball.width]; // */ } else dstPixels[index] = srcPixels[index]; } else{ dstPixels[index] = srcPixels[index]; } } return createImage(new MemoryImageSource(ball.width ,ball.height , dstPixels ,0 ,ball.width)); } public boolean mouseExit(Event evt,int x,int y){ if(parent.isDrawBall){ isChangeBallLocation = true; return true; } return false; } public boolean mouseEnter(Event evt,int x,int y){ if(parent.isDrawBall){ isChangeBallLocation = false; return true; } return false; } public boolean mouseMove(Event evt,int x,int y){ if(parent.isDrawBall){ bx = x - ball.width / 2; by = y - ball.height; return true; } return false; } //改變透明球位置. boolean isChangeBallLocation = true; void changeBallLocation(){ int W_BOUNDS = (xsize - ball.width) * 2; int H_BOUNDS = (ysize - ball.height) * 2; int xDirection,yDirection; if(isChangeBallLocation){ xDirection = ballxDirection; yDirection = ballyDirection; } else{ xDirection = 0; yDirection = 0; } //位移. // /* bx += (int)Math.abs(Math.round(cosT * 2 * xDirection)); by += (int)Math.round(sinT * 2 * yDirection); // */ if( (bx=(bx+random(xDirection)+W_BOUNDS)%W_BOUNDS) >= (W_BOUNDS/2) ) ball.x = (W_BOUNDS-1) - bx; else ball.x = bx; if( (by=(by+random(yDirection)+H_BOUNDS)%H_BOUNDS) >= (H_BOUNDS/2) ) ball.y = (H_BOUNDS-1) - by; else ball.y = by; } //畫出彩色線段. public void drawLine(Graphics gs){ changeLineColor(); gs.setColor(new Color(r,g,b)); changeLineLocation(); gs.drawLine(p1.x,p1.y,p2.x,p2.y); } //改變角度. void setAngle(double dr){ if(dr <= X_DIRECTION || dr <= Y_DIRECTION) rotaryAngle = 20 * Math.PI / 180; else rotaryAngle = Math.min(Math.acos(Y_DIRECTION / dr), Math.asin(X_DIRECTION / dr)); cosR = Math.cos(rotaryAngle); sinR = Math.sin(rotaryAngle); translateAngle += 10 * Math.PI / 180; translateAngle = Math.IEEEremainder(translateAngle,Math.PI * 2); cosT = Math.cos(translateAngle); sinT = Math.sin(translateAngle); } //改變線段的位置. void changeLineLocation(){ int W_BOUNDS = xsize * 2; int H_BOUNDS = ysize * 2; int dx = x2 - x1; int dy = y2 - y1; double dr = Math.sqrt(dx*dx + dy*dy); setAngle(dr); int x0 = x1 + dx / 2;//(x0,y0) 中心座標. int y0 = y1 + dy / 2; int x,y; //對中心點旋轉. // /* x = (int)(Math.round(x1 - x0) * cosR - (y1 - y0) * sinR + x0); y = (int)(Math.round(y1 - y0) * cosR + (x1 - x0) * sinR + y0); x1 = x; y1 = y; x = (int)(Math.round(x2 - x0) * cosR - (y2 - y0) * sinR + x0); y = (int)(Math.round(y2 - y0) * cosR + (x2 - x0) * sinR + y0); x2 = x; y2 = y; // */ if( (x1=(x1+random(X_DIRECTION)+W_BOUNDS)%W_BOUNDS) >= (W_BOUNDS/2) ) p1.x = (W_BOUNDS-1) - x1; else p1.x = x1; if( (x2=(x2+random(X_DIRECTION)+W_BOUNDS)%W_BOUNDS) >= (W_BOUNDS/2) ) p2.x = (W_BOUNDS-1) - x2; else p2.x = x2; if( (y1=(y1+random(Y_DIRECTION)+H_BOUNDS)%H_BOUNDS) >= (H_BOUNDS/2) ) p1.y = (H_BOUNDS-1) - y1; else p1.y = y1; if( (y2=(y2+random(Y_DIRECTION)+H_BOUNDS)%H_BOUNDS) >= (H_BOUNDS/2) ) p2.y = (H_BOUNDS-1) - y2; else p2.y = y2; } //改變線段顏色. void changeLineColor(){ if( (r1=(r1+random(R_DIRECTION))%LINE_COLOR_BOUNDS) >= (LINE_COLOR_BOUNDS/2) ) r = KEEP_COLOR + (LINE_COLOR_BOUNDS-1) - r1; else r = KEEP_COLOR + r1; if( (g1=(g1+random(G_DIRECTION))%LINE_COLOR_BOUNDS) >= (LINE_COLOR_BOUNDS/2) ) g = KEEP_COLOR + (LINE_COLOR_BOUNDS-1) - g1; else g = KEEP_COLOR + g1; if( (b1=(b1+random(B_DIRECTION))%LINE_COLOR_BOUNDS) >= (LINE_COLOR_BOUNDS/2) ) b = KEEP_COLOR + (LINE_COLOR_BOUNDS-1) - b1; else b = KEEP_COLOR + b1; } //改變透明球顏色. int br,bg,bb,br1,bg1,bb1;//透明球顏色用變數. final int CIRCLE_COLOR_BOUNDS = (int)Math.round(0.3d * 255) * 2; void changeBallColor(){ if( (br1=(br1+ random(R_DIRECTION))%CIRCLE_COLOR_BOUNDS) >= (CIRCLE_COLOR_BOUNDS/2) ) br = (CIRCLE_COLOR_BOUNDS-1) - br1; else br = br1; redFactor = br / 255d; if( (bg1=(bg1+ random(G_DIRECTION))%CIRCLE_COLOR_BOUNDS) >= (CIRCLE_COLOR_BOUNDS/2) ) bg = (CIRCLE_COLOR_BOUNDS-1) - bg1; else bg = bg1; greenFactor = bg / 255d; if( (bb1=(bb1+ random(B_DIRECTION))%CIRCLE_COLOR_BOUNDS) >= (CIRCLE_COLOR_BOUNDS/2) ) bb = (CIRCLE_COLOR_BOUNDS-1) - bb1; else bb = bb1; blueFactor = bb / 255d; } }//end of DrawPanel